22.3 清理

借助垃圾回收机制,我们无须考虑Pool收缩问题,只是官方的设计似乎有些粗暴。

mgc.go

func gc(mode int) { systemstack(stopTheWorldWithSema) clearpools() } var poolcleanup func() func clearpools() { // clear sync.Pools if poolcleanup != nil { poolcleanup() } }

这个poolcleanup函数需要额外注册。

mgc.go

//go:linkname sync_runtime_registerPoolCleanup sync.runtime_registerPoolCleanup func sync_runtime_registerPoolCleanup(f func()) { poolcleanup = f }

pool.go

func init() { runtime_registerPoolCleanup(poolCleanup) }

真正的目标是poolCleanup。此时正处于STW状态,所以无须加锁操作。

pool.go

func poolCleanup() { // 遍历所有 Pool 实例 for i, p := range allPools { // 解除引用 allPools[i] = nil // 遍历 Pool.poolLocal 数组 for i := 0; i < int(p.localSize); i++ { // 获取 poolLocal l := indexLocal(p.local, i) // 清理 private 和 share 区域 l.private = nil for j := range l.shared { l.shared[j] = nil } l.shared = nil } // 设置 Pool.local = nil,除了解除所引用的数组空间外, // 还让 Pool.pinSlow 方法将其重新添加到 allPools p.local = nil p.localSize = 0 } // 重置 allPools,需要重新添加所有 Pool.pinSlow allPools = []*Pool{} }

清理操作对已被Get的可达对象没有任何影响,因为两者之间并没有引用关联,留下的缓存对象都属于仅被Pool引用的可移除“白色对象”。

或许我们希望设置一个阈值,仅清理超出数量限制的缓存对象。如此,可避免在垃圾回收后频繁执行New操作。但考虑到此时可能还有一批黑色缓存对象的存在,所以需求也不是那么急切,只是这个Pool的设计显然有进一步改进的余地。